#include "tty.h"
#include "console.h"
#include "dev.h"
#include "kbd.h"
#include "log.h"
#include "sem.h"

static tty_t tty_devs[TTY_NR];

static tty_t* get_tty(device_t* dev)
{
    if (dev->minor < 0 || dev->minor >= TTY_NR || dev->open_count == 0) {
        log_printf("tty is not opened, incorrect tty num = %d\n", dev->minor);
        return (tty_t*)0;
    }
    return &tty_devs[dev->minor];
}

void tty_fifo_init(tty_fifo_t* fifo, char* buf, int size)
{
    fifo->buf = buf;
    fifo->size = size;
    fifo->read = fifo->write = fifo->count = 0;
}

int tty_fifo_put(tty_fifo_t* fifo, char c)
{
    if (fifo->count >= fifo->size) return -1;
    fifo->buf[fifo->write++] = c;
    if (fifo->write >= fifo->size) fifo->write = 0;
    ++fifo->count;
    return 0;
}

int tty_fifo_get(tty_fifo_t* fifo, char* c)
{
    if (fifo->count <= 0) return -1;
    *c = fifo->buf[fifo->read++];
    if (fifo->read >= fifo->size) fifo->read = 0;
    --fifo->count;
    return 0;
}

int tty_open(device_t *dev)
{
    int idex = dev->minor;
    if (idex < 0 || idex >= TTY_NR) {
        log_printf("tty open error, incorrect tty num = %d\n", idex);
        return -1;
    } 

    tty_t* tty = &tty_devs[idex];
    tty_fifo_init(&tty->ififo, tty->ibuf, TTY_IBUF_SIZE);
    sem_init(&tty->osem, TTY_OBUF_SIZE);
    tty_fifo_init(&tty->ofifo, tty->obuf, TTY_OBUF_SIZE);
    tty->console_index = idex;
    tty->oflags = TTY_OCRLF;
    console_init(idex);
    kbd_init();
    return 0;
}

int tty_read(device_t *dev, int addr, char* buf, int size)
{ return size; } 

int tty_write(device_t *dev, int addr, char* buf, int size)
{
    if (size< 0) return -1;
    tty_t* tty = get_tty(dev);
    if (tty == (tty_t*)0) return -1;
    int len = 0;

    while (size--) {
        char c = *buf++;
        if (c == '\n' && (tty->oflags & TTY_OCRLF)) {
            sem_wait(&tty->osem);
            if (tty_fifo_put(&tty->ofifo, '\r') < 0) break;
        }
        sem_wait(&tty->osem);
        int err = tty_fifo_put(&tty->ofifo, c);
        if (err < 0) break;
        ++len;
        console_write(tty);
    }
    return size;
} 

int tty_control(device_t *dev, int cmd, int arg0, int arg1)
{ return 0; } 

void tty_close(device_t *dev)
{} 

dev_desc_t dev_tty_desc = {
    .name = "tty",
    .major = DEV_TTY,
    .open = tty_open,
    .read = tty_read,
    .write = tty_write,
    .control = tty_control,
    .close = tty_close
};
